<?php

declare(strict_types=1);

namespace App\Http\Controller;

use App\Core\Annotation\NoNeedLogin;
use App\Service\AuthAccountService;
use Hyperf\Contract\ContainerInterface;
use Hyperf\Di\Annotation\AnnotationCollector;
use Hyperf\Di\Annotation\Inject;
use Hyperf\HttpServer\Contract\ResponseInterface;
use App\Core\AppKey;

/**
 * 路由自定义调度
 */
class Dispatcher
{
    #[Inject]
    protected AuthAccountService $authAccountService;

    protected $requestMethods = [
        'GET' => 'get',   //获取数据
        'POST' => 'create', //创建数据
        'PUT' => 'update',  //修改数据
        'DELETE' => 'delete', //删除数据
    ];

    /**
     * 配置请求地址与控制器文件夹关系
     * @var array
     */
    protected $uriConfig = [
        [
            'uriPrefix' => 'admin',
            'controllerBaseDirname' => 'AdminPort',
            'openOperationLog' => true, //是否需要开启操作日志
            'userType' => 1
        ],
        [
            'uriPrefix' => 'company',
            'controllerBaseDirname' => 'CompanyPort',
            'openOperationLog' => true, //是否需要开启操作日志
            'userType' => 2
        ],
        [
            'uriPrefix' => 'user',
            'controllerBaseDirname' => 'UserPort',
            'userType' => 3
        ]
    ];

    /**
     * @var ContainerInterface
     */
    protected $container;


    public function __construct(ContainerInterface $container)
    {
        $this->container = $container;
    }

    /**
     * 请求路由调度
     * @return \Psr\Http\Message\ResponseInterface
     */
    public function handle(ResponseInterface $response)
    {
        /*
         * 获取请求控制器
         */
        $controller = $this->getRequestController();

        /*
         * 设置返回对象
         */
        setContext(AppKey::CONTEXT_HTTP_RESPONSE, $response);

        /*
         * 检查登陆
         */
        $this->checkedLoginPower();

        /*
         * 检查是否需要权限判断
         */
        $this->checkedRequestPower($controller, $controller['method'],
            $controller['method'].'/'.implode('/', $controller['params']));

        /*
         * 请求执行控制器
         */
        $data = $controller['controller']->{$controller['method']}(...$controller['params']);
        return $data;
    }

    /**
     * 检查登陆权限
     * @return void
     */
    protected function checkedLoginPower(): void
    {
        $port = getHttpPort();
        $mustLogin = $this->checkedIsMustLogin();
        $token = R()->getHeaderLine('authorization');
        if ($token) {
            $token = str_replace('Bearer ', '', $token);
            setContext(AppKey::CONTEXT_HTTP_AUTH_TOKEN, $token);
        }

        if ( ! $mustLogin && ! $token) {
            return;
        }

        if ($mustLogin && ! $token) {
            responseError('请登录', 401);
        }

        try {
            //web网页授权方式
            $user = $this->authAccountService->checkUpToken($token);
            if ($mustLogin && ! $user && $token) {
                responseError('登录失效，请重新登录', 402);
            }

            if ($mustLogin && ! $user) {
                responseError('请登录', 401);
            }

            $uriConfig = $this->getRequestUriConfig();
            if ($mustLogin && $uriConfig['userType'] != $user['type']) {
                responseError('无权访问', 401);
            }

            setContext(AppKey::CONTEXT_USER, $user);
        } catch (\Exception $e) {
            if ($mustLogin) {
                responseError($e->getMessage(), $e->getCode());
            }
        }
    }

    /**
     * 检查当前访问是否必须登录
     * @return boolean
     */
    protected function checkedIsMustLogin(): bool
    {
        $controller = getContext(AppKey::CONTEXT_HTTP_REQUEST_CONTROLLER);
        $methoed = getContext(AppKey::CONTEXT_HTTP_REQUEST_CONTROLLER_METHOED);
        $annotations = AnnotationCollector::getClassMethodAnnotation(get_class($controller), $methoed);
        return ! $annotations ? true : ! isset($annotations[NoNeedLogin::class]);
    }

    /**
     * 检查访问权限
     * @param string $controller
     * @return void
     */
    protected function checkedRequestPower(): void
    {
        $user = getContext(AppKey::CONTEXT_USER);
        if ( ! $user) {
            return;
        }

        /*
         * 获取或设置权限参数
         */
        $this->getControllerMothedPermissions();

        /*
         * 检查权限
         */
        if ( ! checkPower()) {
            responseError('无操作权限');
        }
    }

    /**
     * 获取控制器方法配置的权限
     * @return array
     */
    protected function getControllerMothedPermissions(): array
    {
        $controller = getContext(AppKey::CONTEXT_HTTP_REQUEST_CONTROLLER);
        $methoed = getContext(AppKey::CONTEXT_HTTP_REQUEST_CONTROLLER_METHOED);
        $annotations = AnnotationCollector::getClassMethodAnnotation(get_class($controller), $methoed);
        $permissions = [];

        if ($annotations) {
            foreach ($annotations as $class => $item) {
                if ($class == Permission::class) {
                    $permissions = $item->value;
                    break;
                }
            }
        }

        setContext(AppKey::CONTEXT_HTTP_REQUEST_CONTROLLER_METHOED_PERMISSION, $permissions);
        return $permissions;
    }

    /**
     * 获取controller
     * @return array
     */
    protected function getRequestController(): array
    {
        $params = $this->getRequestUriParams();
        $controllerFile = dirname(__FILE__).'/'.ucfirst($this->getRequestPort());
        $controller = '\App\Http\Controller\\'.ucfirst($this->getRequestPort());
        $matchController = [];
        $defaultController = 'Index';

        //查找访问的控制器
        foreach ($params as $key => $item){
            $controllerFile .= '/'.ucfirst($item);
            $controller .= '\\'.ucfirst($item);
            unset($params[$key]);
            $hasController = $this->container->has($controller);
            if ($hasController) {
                $matchController[] = [
                    'controller' => make($controller),
                    'controllerClass' => $controller,
                    'params' => $params
                ];
            } else {
                $_controller = $controller.'\\'.$defaultController;
                if ($this->container->has($_controller)) {
                    $matchController[] = [
                        'controller' => $this->container->get($_controller),
                        'controllerClass' => $_controller,
                        'params' => $params
                    ];
                }
            }
        }

        if ( ! $matchController) {
            responseError('请求不存在', 404);
        }

        $controllerPrefix = $this->requestMethods[R()->getMethod()] ?? null;
        $controllerMothed = $controllerPrefix;
        if ( ! $controllerPrefix) {
            responseError('请求不存在', 404);
        }

        $controller = end($matchController);
        $controller['params'] = array_values($controller['params']);
        if (isset($controller['params'][0])) {
            $_mothed = $controllerPrefix.ucfirst($controller['params'][0]);
            if (method_exists($controller['controller'], $_mothed)) {
                unset($controller['params'][0]);
                $controller['params'] = array_values($controller['params']);
                $controllerMothed = $_mothed;
            }
        }

        /*
         * 这一版不允许传方法参数
         */
        if ($controller['params']) {
            responseError('请求不存在', 404);
        }


        if ( ! method_exists($controller['controller'], $controllerMothed)) {
            responseError('请求不存在', 404);
        }

        //设置当前请求的功能模块
        if (property_exists($controller['controllerClass'], 'MODULE')) {
            $_class = $controller['controllerClass'];
            setContext(AppKey::CONTEXT_REQUEST_MODULE_KEY, ($_class::$MODULE ?? null));
        }

        //设置当前请求的控制器对象
        setContext(AppKey::CONTEXT_HTTP_REQUEST_CONTROLLER, $controller['controller']);

        //设置当前请求的控制器方法
        setContext(AppKey::CONTEXT_HTTP_REQUEST_CONTROLLER_METHOED, $controllerMothed);

        return [
            'controller' => $controller['controller'],
            'method' => $controllerMothed,
            'params' => $controller['params']
        ];
    }

    /**
     * 获取请求模块
     * @return string
     */
    protected function getRequestPort(): string
    {
        $port = getContext(AppKey::CONTEXT_HTTP_REQUEST_PORT);
        if ($port) {
            return $port;
        }

        $port = $this->getRequestUriConfig()['controllerBaseDirname'];
        setContext(AppKey::CONTEXT_HTTP_REQUEST_PORT, $port);
        return $port;
    }

    /**
     * 获取当前请求的uri配置
     * @return array
     */
    protected function getRequestUriConfig(): array
    {
        $config = [];
        $uri = $this->getRequestUri();
        foreach ($this->uriConfig as $item) {
            if (strpos($uri, $item['uriPrefix'].'/') !== 0) {
                continue;
            }

            $config = $item;
            break;
        }

        if ( ! $config) {
            responseError('请求地址不存在', 404);
        }

        //当前请求是否需要操作日志
        setContext(AppKey::CONTEXT_REQUEST_HAS_OPERATION_LOG, $config['openOperationLog'] ?? false);
        return $config;
    }

    /**
     * 获取请求的uri参数
     * @return array
     */
    protected function getRequestUriParams(): array
    {
        $config = $this->getRequestUriConfig();
        $site = strpos($this->getRequestUri(),$config['uriPrefix']);
        $tS  = explode('/', $this->getRequestUri());
        unset($tS[$site]);
        return $tS;
    }

    /**
     * 获取请求的uri
     * @return string
     */
    protected function getRequestUri() :string
    {
        return convertHorizontalline(trim(R()->getUri()->getPath(), '/'), false);
    }
}
